#lang racket (require rackunit) (require "extras.rkt") ;; starting point: 10-9-interfaces-revisited.rkt ;; This illustrates the use of interfaces to hide object ;; implementations (define StupidRobot<%> (interface () ;; a new StupidRobot<%> is required to start at position 0 ;; -> StupidRobot<%> ;; RETURNS: a Robot just like this one, except moved one position ;; to the right move-right ;; -> Integer ;; RETURNS: the current x-position of the robot get-pos ;;;;;;;;;;;;;;;; items below here are for testing only ;; -> Integer for-test:get-version )) ;; Here are 3 implementations of StupidRobot<%>. ;; represent the position by a field x. (define Robot1% (class* object% (StupidRobot<%>) (init-field [x 0]) ;; interp: the position of the robot. (super-new) (define/public (move-right) (new Robot1% [x (+ x 1)])) (define/public (get-pos) x) ;; for testing only (define/public (for-test:get-version) 1) )) (define Robot2% (class* object% (StupidRobot<%>) (init-field [y 0]) ;; interp: the negative of the position of ;; the robot. (super-new) (define/public (move-right) (new Robot2% [y (- y 1)])) (define/public (get-pos) (- y)) ;; for testing only (define/public (for-test:get-version) 2) )) (define Robot3% (class* object% (StupidRobot<%>) (init-field [x empty]) ;; a list whose length is equal to the ;; position of the robot ;; Puzzle: if the robot could move left, and therefore reach ;; negative positions, how could you adapt this representation to ;; handle this? (super-new) (define/public (move-right) (new Robot3% [x (cons 1 x)])) (define/public (get-pos) (length x)) ;; for testing only (define/public (for-test:get-version) 3) )) ;; -> StupidRobot<%> (define (new-robot) (local ((define i (random 3))) ;; generates numbers in [0,k-1] (cond [(= i 0) (new Robot1%)] [(= i 1) (new Robot2%)] [(= i 2) (new Robot3%)]))) (define Factory<%> (interface () new-robot ; -> StupidRobot<%> for-test:count ; -> Integer ; returns the number of robots built by this factory so far. )) ;; A Factory is a (new Factory%) (define Factory% (class* object% (Factory<%>) (field [count 0]) ;; interp: the number of robots built by this factory so far. (super-new) ; -> StupidRobot<%> (define/public (new-robot) (set! count (+ count 1)) (local ((define i (modulo (- count 1) 3))) (cond [(= i 0) (new Robot1%)] [(= i 1) (new Robot2%)] [(= i 2) (new Robot3%)]))) ; -> Integer (define/public (for-test:count) count) )) (define f1 (new Factory%)) ;;;;;;;;;; TESTS ;;;;;;;;;;; (begin-for-test (local ((define f1 (new Factory%))) (check-equal? (send f1 for-test:count) 0) (send f1 new-robot) (send f1 new-robot) (send f1 new-robot) (send f1 new-robot) (send f1 new-robot) (check-equal? (send f1 for-test:count) 5)) (local ((define f1 (new Factory%)) (define robots (build-list 6 (lambda (i) (send f1 new-robot)))) (define versions (map (lambda (r) (send r for-test:get-version)) robots))) (check-equal? (length robots) 6) (check-equal? versions '(1 2 3 1 2 3))) )